/**
 * Copyright (c) 2017-2024 Nop Platform. All rights reserved.
 * Author: canonical_entropy@163.com
 * Blog:   https://www.zhihu.com/people/canonical-entropy
 * Gitee:  https://gitee.com/canonical-entropy/nop-entropy
 * Github: https://github.com/entropy-cloud/nop-entropy
 */
package com.gitee.wsl.struct.table.tree

import com.gitee.wsl.struct.table.IRow
import com.gitee.wsl.struct.table.ITable
import kotlin.math.max

/**
 * TreeTable布局
 *
 * @param <T>
</T> */
class TreeTableLayout<T> {
    /**
     * 根据子单元格大小和位置计算父单元格的大小和位置。父单元格和子单元格构成一个TreeCellBox，子TreeCellBox完全嵌套在父TreeCellBox内部
     *
     * @param cells 根单元格
     */
    fun calcLayout(cells: MutableList<TreeCell>, bVer: Boolean): TreeCell {
        // 创建一个辅助单元格，用于简化计算
        val dummy = TreeCell(null, if (bVer) TreeCellChildPosition.ver else TreeCellChildPosition.hor)
        dummy.treeLevel = -1
        //dummy.children = cells as MutableList<TreeNode>
        dummy.addAll(cells)

        // 1. 计算TreeCell的BoundingBox的大小
        calcBbox(dummy)
        // 2. 对其子单元格和父单元格的BoundingBox, 必要时自动伸展子单元格，确保没有空隙
        adjustBbox(dummy, dummy.bboxWidth, dummy.bboxHeight)
        // 3. 计算每个单元格的rowIndex和colIndex
        updatePos(dummy, 0, 0)
        return dummy
    }

    fun assignToTable(cells: List<TreeCell?>, table: ITable<IRow>) {
        /*for (cell in cells) {
            for (c in TreeVisitors.depthFirstIterator(cell, true)) {
                if (c.isVirtual) continue
                table.setCell(c.rowIndex, c.colIndex, cell)
            }
        }*/
    }

    /**
     * 计算单元格以及所有子单元格构成的总区域的大小
     */
    fun calcBbox(cell: TreeCell) {
        val children = cell.children as MutableList<TreeCell>
        if (children.isEmpty()) {
            if (cell.isVirtual) {
                cell.bboxHeight = 0
                cell.bboxHeight = 0
            } else {
                cell.bboxWidth = cell.colSpan
                cell.bboxHeight = cell.rowSpan
            }
            return
        }

        for (child in children) {
            child.treeLevel += 1
            calcBbox(child)
        }

        when (cell.childPos) {
            TreeCellChildPosition.right_hor, TreeCellChildPosition.left_hor -> {
                val w = sumBboxWidth(children)
                val h = maxBboxHeight(children)
                cell.mergeDown = max(cell.mergeDown, (h - 1))
                cell.bboxWidth = cell.colSpan + w
                cell.bboxHeight = cell.rowSpan
            }

            TreeCellChildPosition.right_ver, TreeCellChildPosition.left_ver -> {
                val w = maxBboxWidth(children)
                val h = sumBboxWidth(children)
                cell.mergeDown = max(cell.mergeDown, (h - 1))
                cell.bboxWidth = cell.colSpan + w
                cell.bboxHeight = cell.rowSpan
            }

            TreeCellChildPosition.bottom_hor, TreeCellChildPosition.top_hor -> {
                val w = sumBboxWidth(children)
                val h = maxBboxHeight(children)
                cell.mergeAcross = max(cell.mergeAcross, (w - 1))
                cell.bboxWidth = cell.colSpan
                cell.bboxHeight = cell.rowSpan + h
            }

            TreeCellChildPosition.bottom_ver, TreeCellChildPosition.top_ver -> {
                val w = maxBboxWidth(children)
                val h = sumBboxWidth(children)
                cell.mergeAcross = max(cell.mergeAcross, (w - 1))
                cell.bboxWidth = cell.colSpan
                cell.bboxHeight = cell.rowSpan + h
            }

            TreeCellChildPosition.hor -> {
                val w = sumBboxWidth(children)
                val h = maxBboxHeight(children)
                cell.mergeAcross = 0
                cell.mergeDown = 0
                cell.bboxWidth = w
                cell.bboxHeight = h
            }

            TreeCellChildPosition.ver -> {
                val w = maxBboxWidth(children)
                val h = sumBboxWidth(children)
                cell.mergeAcross = 0
                cell.mergeDown = 0
                cell.bboxWidth = w
                cell.bboxHeight = h
            }

            else -> throw IllegalArgumentException("ERR_TREE_TABLE_UNSUPPORTED_CHILD_POS")
                /*.param(
                ARG_POS,
                cell.getChildPos())*/
        }
    }

    fun sumBboxWidth(cells: List<TreeCell>): Int {
        var ret = 0
        for (cell in cells) {
            ret += cell.bboxWidth
        }
        return ret
    }

    fun sumBboxHeight(cells: List<TreeCell>): Int {
        var ret = 0
        for (cell in cells) {
            ret += cell.bboxHeight
        }
        return ret
    }

    fun maxBboxWidth(cells: List<TreeCell>): Int {
        var ret = 0
        for (cell in cells) {
            if (cell.bboxWidth > ret) ret = cell.bboxWidth
        }
        return ret
    }

    fun maxBboxHeight(cells: List<TreeCell>): Int {
        var ret = 0
        for (cell in cells) {
            if (cell.bboxHeight > ret) ret = cell.bboxHeight
        }
        return ret
    }

    /**
     * 根据boundingBox的计算结果，自动拉伸部分单元格，使其占满所有剩余空间
     */
    fun adjustBbox(cell: TreeCell, w: Int, h: Int) {
        val children = cell.children as List<TreeCell>
        if (children.isEmpty()) {
            cell.mergeDown = h - 1
            cell.mergeAcross = w - 1
            cell.bboxHeight = h
            cell.bboxWidth = w
            return
        }

        val bboxWidth: Int = cell.bboxWidth
        val bboxHeight: Int = cell.bboxHeight

        cell.bboxWidth = w
        cell.bboxHeight = h

        when (cell.childPos) {
            TreeCellChildPosition.right_hor, TreeCellChildPosition.left_hor -> {
                // 如果边框宽度增加了，则延展最后一个child
                lastChild(children).incWidth(w - bboxWidth)
                adjustChildrenHor(children, h)
                cell.mergeDown = h - 1
            }

            TreeCellChildPosition.right_ver, TreeCellChildPosition.left_ver -> {
                lastChild(children).incHeight(h - bboxHeight)
                adjustChildrenVer(children, w - cell.colSpan)
                cell.mergeDown = h - 1
            }

            TreeCellChildPosition.bottom_hor, TreeCellChildPosition.top_hor -> {
                lastChild(children).incWidth(w - bboxWidth)
                adjustChildrenHor(children, h - cell.rowSpan)
                cell.mergeAcross = w - 1
            }

            TreeCellChildPosition.bottom_ver, TreeCellChildPosition.top_ver -> {
                cell.mergeAcross = w - 1
                lastChild(children).incHeight(h - bboxHeight)
                adjustChildrenVer(children, w)
            }

            TreeCellChildPosition.hor -> {
                lastChild(children).incWidth(w - bboxWidth)
                adjustChildrenHor(children, h)
            }

            TreeCellChildPosition.ver -> {
                lastChild(children).incHeight(h - bboxHeight)
                adjustChildrenVer(children, w)
            }

            else -> throw IllegalArgumentException("ERR_TREE_TABLE_UNSUPPORTED_CHILD_POS")
                /*.param(
                ARG_POS,
                cell.getChildPos())*/
        }
    }

    fun lastChild(children: List<TreeCell>): TreeCell {
        return children[children.size - 1]
    }

    fun adjustChildrenHor(children: List<TreeCell>, h: Int) {
        for (child in children) {
            adjustBbox(child, child.bboxWidth, h)
        }
    }

    fun adjustChildrenVer(children: List<TreeCell>, w: Int) {
        for (child in children) {
            adjustBbox(child, w, child.bboxHeight)
        }
    }

    fun updatePos(cell: TreeCell, rowIndex: Int, colIndex: Int) {
        cell.rowIndex = rowIndex
        cell.colIndex = colIndex

        val children = cell.children as List<TreeCell>
        if (children.isEmpty()) return

        val bboxWidth: Int = cell.bboxWidth
        val bboxHeight: Int = cell.bboxHeight

        when (cell.childPos) {
            TreeCellChildPosition.right_hor -> {
                setHorChildrenTo(children, rowIndex, colIndex + cell.colSpan)
            }

            TreeCellChildPosition.left_hor -> {
                cell.colIndex = colIndex + bboxWidth - cell.colSpan
                setHorChildrenTo(children, rowIndex, colIndex)
            }

            TreeCellChildPosition.right_ver -> {
                setVerChildrenTo(children, rowIndex, colIndex + cell.colSpan)
            }

            TreeCellChildPosition.left_ver -> {
                cell.colIndex = colIndex + bboxWidth - cell.colSpan
                setVerChildrenTo(children, rowIndex, colIndex)
            }

            TreeCellChildPosition.bottom_hor -> {
                setHorChildrenTo(children, rowIndex + cell.rowSpan, colIndex)
            }

            TreeCellChildPosition.top_hor -> {
                cell.rowIndex = rowIndex + bboxHeight - cell.rowSpan
                setHorChildrenTo(children, rowIndex, colIndex)
            }

            TreeCellChildPosition.bottom_ver -> {
                setVerChildrenTo(children, rowIndex + cell.rowSpan, colIndex)
            }

            TreeCellChildPosition.top_ver -> {
                cell.rowIndex = rowIndex + bboxHeight - cell.rowSpan
                setVerChildrenTo(children, rowIndex, colIndex)
            }

            TreeCellChildPosition.hor -> {
                setHorChildrenTo(children, rowIndex, colIndex)
            }

            TreeCellChildPosition.ver -> {
                setVerChildrenTo(children, rowIndex, colIndex)
            }

            else -> throw IllegalArgumentException("ERR_TREE_TABLE_UNSUPPORTED_CHILD_POS")
                /*.param(
                ARG_POS,
                cell.getChildPos())*/
        }
    }

    fun setHorChildrenTo(children: List<TreeCell>, rowIndex: Int, colIndex: Int) {
        var colIndex = colIndex
        for (child in children) {
            updatePos(child, rowIndex, colIndex)
            colIndex += child.bboxWidth
        }
    }

    fun setVerChildrenTo(children: List<TreeCell>, rowIndex: Int, colIndex: Int) {
        var rowIndex = rowIndex
        for (child in children) {
            updatePos(child, rowIndex, colIndex)
            rowIndex += child.bboxHeight
        }
    }
}